package com.hunyuan.player;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.PowerManager;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.text.TextUtils;
import android.util.Log;

/**
 * Created by hunyuan on 2017/1/20.
 */

public class LocalPlayback implements Playback,
        AudioManager.OnAudioFocusChangeListener,
        MediaPlayer.OnCompletionListener,
        MediaPlayer.OnErrorListener,
        MediaPlayer.OnPreparedListener,
        MediaPlayer.OnSeekCompleteListener {

    private static final String TAG = LocalPlayback.class.getSimpleName();

    // 失去焦点，但是可以降低音量而不是停止播放
    public static final float VOLUME_DUCK = 0.2f;
    // 获得焦点，设置正常音量
    public static final float VOLUME_NORMAL = 1.0f;

    // 无焦点，不可以降低音量播放
    private static final int AUDIO_NO_FOCUS_NO_DUCK = 0;
    // 无焦点，可以降低音量播放
    private static final int AUDIO_NO_FOCUS_CAN_DUCK = 1;
    // 拥有所有焦点
    private static final int AUDIO_FOCUSED = 2;

    private volatile boolean mAudioNoisyReceiverRegistered; //是否注册了接收器
    private final WifiManager.WifiLock mWifiLock; // wifi锁，用于在访问网络时，防止wifi休眠
    private volatile String mCurrentMediaId; // 当前播放的MediaId
    private volatile int mCurrentPosition; // 当前播放的进度
    private boolean mPlayOnFocusGain; // 在获得焦点时播放
    private final Context mContext;
    private Callback mCallback;
    private int mState; // 播放状态

    private int mAudioFocus = AUDIO_NO_FOCUS_NO_DUCK; // 焦点类型
    private final AudioManager mAudioManager; // 音频管理
    private MediaPlayer mMediaPlayer; // 播放器

    private final IntentFilter mAudioNoisyIntentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);

    private final BroadcastReceiver mAudioNoisyReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
                Log.d(TAG, "Headphones disconnected.");
                if (isPlaying()) {
                    Intent i = new Intent(context, PlayService.class);
                    i.setAction(PlayService.ACTION_CMD);
                    i.putExtra(PlayService.CMD_NAME, PlayService.CMD_PAUSE);
                    mContext.startService(i);
                }
            }
        }
    };

    public LocalPlayback(Context context) {
        this.mContext = context;
        this.mWifiLock = ((WifiManager) context.getSystemService(Context.WIFI_SERVICE))
                .createWifiLock(WifiManager.WIFI_MODE_FULL, "uAmp_lock");
        this.mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        this.mState = PlaybackStateCompat.STATE_NONE;
    }

    // ==================  Playback接口  ==================

    @Override
    public void start() {

    }

    /**
     * 停止播放，修改状态，修改当前进度，放弃焦点，反注册接收器，释放资源
     *
     * @param notifyListeners if true and a callback has been set by setCallback,
     *                        callback.onPlaybackStatusChanged will be called after changing
     */
    @Override
    public void stop(boolean notifyListeners) {
        mState = PlaybackStateCompat.STATE_STOPPED;
        if (notifyListeners && mCallback != null) {
            mCallback.onPlaybackStatusChanged(mState);
        }
        mCurrentPosition = getCurrentStreamPosition();

        //放弃焦点
        giveUpAudioFocus();
        //反注册接收器
        unregisterAudioNoisyReceiver();
        //释放资源
        relaxResources(true);
    }

    @Override
    public void setState(int state) {
        this.mState = state;
    }

    @Override
    public int getState() {
        return mState;
    }

    @Override
    public boolean isConnected() {
        return true;
    }

    /**
     * 是否播放
     * 判断条件：mPlayOnFocusGain是否为true 或者 mMediaPlayer是否在播放
     *
     * @return
     */
    @Override
    public boolean isPlaying() {
        return mPlayOnFocusGain || (mMediaPlayer != null && mMediaPlayer.isPlaying());
    }

    @Override
    public int getCurrentStreamPosition() {
        return mMediaPlayer != null ? mMediaPlayer.getCurrentPosition() : mCurrentPosition;
    }

    @Override
    public void setCurrentStreamPosition(int pos) {
        this.mCurrentPosition = pos;
    }

    @Override
    public void updateLastKnownStreamPosition() {
        if (mMediaPlayer != null)
            mCurrentPosition = mMediaPlayer.getCurrentPosition();
    }

    /**
     * @param item to play
     */
    @Override
    public void play(MediaSessionCompat.QueueItem item) {
        mPlayOnFocusGain = true;
        tryToGetAudioFocus();
        registerAudioNoisyReceiver();
        String mediaId = item.getDescription().getMediaId();
        boolean mediaHasChanged = !TextUtils.equals(mediaId, mCurrentMediaId);
        if (mediaHasChanged) {
            mCurrentMediaId = mediaId;
            mCurrentPosition = 0;
        }
        if (mState == PlaybackStateCompat.STATE_PAUSED && !mediaHasChanged && mMediaPlayer != null) {
            configMediaPlayerState();
        } else {
            mState = PlaybackStateCompat.STATE_STOPPED;
            relaxResources(false);
            MediaMetadataCompat music = MusicFactory.getMediaMetadataById(mCurrentMediaId);
            String source = music.getString(MediaMetadataCompat.METADATA_KEY_ART_URI);

            try {
                createMediaPlayerIfNeeded();

                mState = PlaybackStateCompat.STATE_BUFFERING;

                mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                mMediaPlayer.setDataSource(source);

                // 开始异步准备，当准备完毕后，调用OnPreparedListener的onPrepared()方法
                // 在没准备完成之前，不能调用start()方法
                mMediaPlayer.prepareAsync();

                // 如果音乐是在互联网上，应该要持有一个WiFi锁，防止WiFi休眠
                mWifiLock.acquire();

                if (mCallback != null)
                    mCallback.onPlaybackStatusChanged(mState);
            } catch (Exception e) {
                if (mCallback != null)
                    mCallback.onError(e.getMessage());
            }
        }
    }

    @Override
    public void pause() {
        if (mState == PlaybackStateCompat.STATE_PLAYING) {
            if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
                mMediaPlayer.pause();
                mCurrentPosition = mMediaPlayer.getCurrentPosition();
            }
            relaxResources(false);
        }
        mState = PlaybackStateCompat.STATE_PAUSED;
        if (mCallback != null)
            mCallback.onPlaybackStatusChanged(mState);
        unregisterAudioNoisyReceiver();
    }

    @Override
    public void seekTo(int position) {
        if (mMediaPlayer == null) {
            mCurrentPosition = position;
        } else {
            if (mMediaPlayer.isPlaying())
                mState = PlaybackStateCompat.STATE_BUFFERING;
            registerAudioNoisyReceiver();
            mMediaPlayer.seekTo(position);
            if (mCallback != null)
                mCallback.onPlaybackStatusChanged(mState);
        }
    }

    @Override
    public void setCurrentMediaId(String mediaId) {
        this.mCurrentMediaId = mediaId;
    }

    @Override
    public String getCurrentMediaId() {
        return mCurrentMediaId;
    }

    @Override
    public void setCallback(Callback callback) {
        this.mCallback = callback;
    }


    // ================== MediaPlayer 和 Audio 的监听接口 ==================

    @Override
    public void onAudioFocusChange(int focusChange) {
        if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
            mAudioFocus = AUDIO_FOCUSED;
        } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS ||
                focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT ||
                focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {

            boolean canDuck = focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
            mAudioFocus = canDuck ? AUDIO_NO_FOCUS_CAN_DUCK : AUDIO_NO_FOCUS_NO_DUCK;

            if (mState == PlaybackStateCompat.STATE_PLAYING && !canDuck) {
                mPlayOnFocusGain = true;
            }
        } else {
            Log.d(TAG, "onAudioFocusChange: ignore");
        }
        configMediaPlayerState();
    }

    @Override
    public void onCompletion(MediaPlayer mediaPlayer) {
        if (mCallback != null)
            mCallback.onCompletion();
    }

    @Override
    public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
        if (mCallback != null)
            mCallback.onError("MediaPlayer error " + what + " (" + extra + ")");
        return true;
    }

    @Override
    public void onPrepared(MediaPlayer mediaPlayer) {
        configMediaPlayerState();
    }

    @Override
    public void onSeekComplete(MediaPlayer mediaPlayer) {
        mCurrentPosition = mediaPlayer.getCurrentPosition();
        if (mState == PlaybackStateCompat.STATE_BUFFERING) {
            registerAudioNoisyReceiver();
            mMediaPlayer.start();
            mState = PlaybackStateCompat.STATE_PLAYING;
        }
        if (mCallback != null)
            mCallback.onPlaybackStatusChanged(mState);
    }


    //======================

    /**
     * Give up the audio focus.
     */
    private void giveUpAudioFocus() {
        Log.d(TAG, "giveUpAudioFocus");
        if (mAudioManager.abandonAudioFocus(this) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            mAudioFocus = AUDIO_NO_FOCUS_NO_DUCK;
        }
    }

    /**
     * 释放资源
     *
     * @param releaseMediaPlayer 是否释放播放器资源
     */
    private void relaxResources(boolean releaseMediaPlayer) {
        Log.d(TAG, "relaxResources. releaseMediaPlayer=" + releaseMediaPlayer);

        // stop and release the Media Player, if it's available
        if (releaseMediaPlayer && mMediaPlayer != null) {
            mMediaPlayer.reset();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }

        // we can also release the Wifi lock, if we're holding it
        if (mWifiLock.isHeld()) {
            mWifiLock.release();
        }
    }

    /**
     * 尝试获取焦点
     * 获取成功则置为 AUDIO_FOCUSED
     * 获取失败则是 AUDIO_NO_FOCUS_NO_DUCK
     */
    private void tryToGetAudioFocus() {
        int result = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
                AudioManager.AUDIOFOCUS_GAIN);
        if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            mAudioFocus = AUDIO_FOCUSED;
        } else {
            mAudioFocus = AUDIO_NO_FOCUS_NO_DUCK;
        }
    }

    /**
     * 设置播放器的状态
     * <p>
     * 1. 如果焦点类型为AUDIO_NO_FOCUS_NO_DUCK，并且正在播放，则暂停播放
     * 2. 如果焦点类型为AUDIO_NO_FOCUS_CAN_DUCK，降低音量播放
     * 3. 如果焦点类型为AUDIO_FOCUSED，正常音量播放
     * <p>
     * 在允许在获得焦点时播放，播放器未在播放的情况下：
     * 如果保存的进度与实际进度相符，则开始播放
     * 如果不符，则seekTo保存的进度，并修改当前状态为STATE_BUFFERING
     */
    private void configMediaPlayerState() {
        if (mAudioFocus == AUDIO_NO_FOCUS_NO_DUCK) {
            if (mState == PlaybackStateCompat.STATE_PLAYING)
                pause();
        } else {
            registerAudioNoisyReceiver();
            if (mAudioFocus == AUDIO_NO_FOCUS_CAN_DUCK) {
                mMediaPlayer.setVolume(VOLUME_DUCK, VOLUME_DUCK);
            } else {
                if (mMediaPlayer != null) {
                    mMediaPlayer.setVolume(VOLUME_NORMAL, VOLUME_NORMAL);
                }
            }
        }

        if (mPlayOnFocusGain) {
            if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
                if (mCurrentPosition == mMediaPlayer.getCurrentPosition()) {
                    mMediaPlayer.start();
                } else {
                    mMediaPlayer.seekTo(mCurrentPosition);
                    mState = PlaybackStateCompat.STATE_BUFFERING;
                }
            }
            mPlayOnFocusGain = false;
        }

        if (mCallback != null)
            mCallback.onPlaybackStatusChanged(mState);
    }

    /**
     * 如果mMediaPlayer不存在，则新建并设置监听器
     * 否则reset()
     */
    private void createMediaPlayerIfNeeded() {
        if (mMediaPlayer == null) {
            mMediaPlayer = new MediaPlayer();
            //确保播放器在播放时获取了wake-lock，CPU在音乐播放时可能会睡死，导致播放器停止
            mMediaPlayer.setWakeMode(mContext.getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

            mMediaPlayer.setOnPreparedListener(this);
            mMediaPlayer.setOnCompletionListener(this);
            mMediaPlayer.setOnErrorListener(this);
            mMediaPlayer.setOnSeekCompleteListener(this);
        } else {
            mMediaPlayer.reset();
        }
    }

    /**
     * 注册音频噪声接收器
     */
    private void registerAudioNoisyReceiver() {
        if (!mAudioNoisyReceiverRegistered) {
            mContext.registerReceiver(mAudioNoisyReceiver, mAudioNoisyIntentFilter);
            mAudioNoisyReceiverRegistered = true;
        }
    }

    /**
     * 反注册音频噪声接收器
     */
    private void unregisterAudioNoisyReceiver() {
        if (mAudioNoisyReceiverRegistered) {
            mContext.unregisterReceiver(mAudioNoisyReceiver);
            mAudioNoisyReceiverRegistered = false;
        }
    }
}
